package org.ala.tiktools.rpc.spring;

import java.lang.reflect.Proxy;

import org.ala.tiktools.logs.LoggerFactory;
import org.ala.tiktools.rpc.client_server.client.AlaRpcClient;
import org.ala.tiktools.rpc.client_server.client.RpcClientProxy;
import org.ala.tiktools.rpc.config.RpcConfigInfo;
import org.ala.tiktools.rpc.plugin.load_balance.AbstractLoadBalance;
import org.ala.tiktools.rpc.plugin.load_balance.ILoadBalance;
import org.ala.tiktools.rpc.plugin.load_balance.selector.ISelector;
import org.ala.tiktools.rpc.plugin.node.INodeCreator;
import org.ala.tiktools.tools.BeanTools;
import org.ala.tiktools.tools.ReflectionTools;
import org.slf4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

/**
 * 生成RpcClient代理的factory
 *
 * @author ala
 * @date 2025-04-04 22:45
 */
public class RpcClientFactoryBean implements FactoryBean<Object>, ApplicationContextAware, EnvironmentAware {


    static Logger log = LoggerFactory.getLogger("rpc", "init");


    /**
     * 被代理的接口类型
     */
    protected Class<?> type;
    /**
     *
     */
    protected ApplicationContext context;
    /**
     *
     */
    protected Environment env;


    @Override
    public Object getObject() throws Exception {
        return Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, buildProxy());
    }


    /**
     * 构建客户端代理
     */
    protected RpcClientProxy buildProxy() {
        AlaRpcClient anno = type.getAnnotation(AlaRpcClient.class);

        RpcClientProxy proxy = new RpcClientProxy();
        proxy.setContext(context);
        proxy.setGroup(StringUtils.isEmpty(anno.name()) ? anno.hosts() : anno.name());
        proxy.setRootUrl(anno.path());

        //  给loadBalance
        proxy.setLoadBalance(getLoadBalance(anno));
        //  给节点创建器
        if (proxy.getLoadBalance() instanceof AbstractLoadBalance) {
            ((AbstractLoadBalance)proxy.getLoadBalance()).setNodeCreator(getNodeCreator(anno));
            ((AbstractLoadBalance)proxy.getLoadBalance()).setSelector(getSelector(anno));
        }

        log.info("[RpcClientFactoryBean buildProxy] 初始化RpcClient代理 name:{} hosts:{}", anno.name(), anno.hosts());
        proxy.getLoadBalance().loadRpcClient(anno);
        return proxy;
    }


    /**
     * 取LoadBalance
     */
    protected ILoadBalance getLoadBalance(AlaRpcClient anno) {
        //  如果bean自己配了anno，直接取bean
        if (anno.loadBalanceClass() != null && anno.loadBalanceClass().length > 0) {
            ILoadBalance loadBalance = ReflectionTools.newInstance(anno.loadBalanceClass()[0]);
            loadBalance.setApplicationContext(context);
            loadBalance.setEnvironment(env);
            return loadBalance;
        }
        //  如果配了私有链接，则每次new一个新的
        //  此时不会依赖spring，所以需要自行给初始化
        else if (anno.privately()) {
            ILoadBalance loadBalance = ReflectionTools.newInstance(RpcConfigInfo.INSTANCE.getLoadBalanceClass());
            loadBalance.setApplicationContext(context);
            loadBalance.setEnvironment(env);
            return loadBalance;
        }
        return context.getBean(RpcBeanConfiguration.BEAN_LOAD_BALANCE, ILoadBalance.class);
    }
    /**
     * 取节点创建器
     */
    @SuppressWarnings("rawtypes")
	protected INodeCreator getNodeCreator(AlaRpcClient anno) {
        //  如果bean自己配了anno，直接取bean
        if (anno.nodeCreator() != null && anno.nodeCreator().length > 0) {
            return BeanTools.getBeanOrNew(context, anno.nodeCreator()[0]);
        }
        return context.getBean(RpcBeanConfiguration.BEAN_NODE_CREATOR, INodeCreator.class);
    }
    /**
     * 取选择器
     */
    protected ISelector getSelector(AlaRpcClient anno) {
        //  如果bean自己配了anno，直接取bean
        if (anno.nodeSelector() != null && anno.nodeSelector().length > 0) {
            return BeanTools.getBeanOrNew(context, anno.nodeSelector()[0]);
        }
        return context.getBean(RpcBeanConfiguration.BEAN_NODE_SELECTOR, ISelector.class);
    }


    @Override
    public Class<?> getObjectType() {
        return type;
    }

    public Class<?> getType() {
        return type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    @Override
    public void setEnvironment(Environment environment) {
        env = environment;
    }
}
